/*
 * Copyright 2012 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.dspl.validate.validator;

import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.dspl.model.Table;
import com.google.dspl.model.Table.Column;
import com.google.dspl.validate.ItemReference;
import com.google.dspl.validate.ValidationContext;
import com.google.dspl.validate.violation.TableNoColumnsViolation;
import com.google.dspl.validate.violation.TableNoIdViolation;
import com.google.dspl.validate.violation.Violation;

import java.util.List;

/**
 * {@link Validator} for {@link Table}s.
 *
 * @author James Grimm
 */
public class TableValidator implements Validator<Table> {

  public interface ColumnValidatorsFactory {
    List<Validator<Column>> create(Table table);
  }

  private ColumnValidatorsFactory columnValidatorsFactory;

  public TableValidator(ColumnValidatorsFactory columnValidatorFactory) {
    this.columnValidatorsFactory = columnValidatorFactory;
  }

  @Override
  public List<Violation> validate(Table table, ValidationContext context) {
    ImmutableList.Builder<Violation> violations = ImmutableList.builder();
    validateId(table, context, violations);
    validateInfo(table, context, violations);
    validateTableHasColumns(table, context, violations);
    validateColumns(table, context, violations);

    return violations.build();
  }

  private void validateId(Table table, ValidationContext context,
      ImmutableList.Builder<Violation> violations) {
    ItemReference tableReference = tableReference(table, context);
    if (Strings.isNullOrEmpty(table.getId())) {
      violations.add(new TableNoIdViolation(tableReference));
    } else {
      IdValidator validator = new IdValidator(tableReference);
      validator.validate(table.getId(), violations);
    }
  }

  private void validateInfo(Table table, ValidationContext context,
      ImmutableList.Builder<Violation> violations) {
    InfoValidator validator = new InfoValidator(tableReference(table, context), false);
    validator.validate(table.getInfo(), violations);
  }

  private void validateTableHasColumns(
      Table table, ValidationContext context, ImmutableList.Builder<Violation> violations) {
    if (table.getColumns().isEmpty()) {
      violations.add(
          new TableNoColumnsViolation(tableReference(table, context), table.getId()));
    }
  }

  private void validateColumns(Table table, ValidationContext context,
      ImmutableList.Builder<Violation> result) {
    UniqueIdViolationAccumulator uniqueIdValidator = new UniqueIdViolationAccumulator();

    List<Validator<Column>> columnValidators = columnValidatorsFactory.create(table);
    for (Column column : table.getColumns()) {
      ItemReference columnItemReference =
          ItemReference.reference(context.getCurrentDataset(), table, column);
      uniqueIdValidator.addId(column.getId(), columnItemReference);

      for (Validator<Column> columnValidator : columnValidators) {
        result.addAll(columnValidator.validate(column, context));
      }
    }

    result.addAll(uniqueIdValidator.getViolations());
  }

  private ItemReference tableReference(Table table, ValidationContext context) {
    return ItemReference.reference(context.getCurrentDataset(), table);
  }
}
